package simulation;

import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import util.Location;
import util.Sprite;
import view.Canvas;

/**
 * XXX.
 * 
 * @author Robert C. Duvall
 */
public class Model {
    /** */
    public static final int QUARTER_TURN = 90;
    /** */
    public static final int HALF_TURN = 180;
    private static Dimension ourBounds;
    // bounds and input for game
    private Canvas myView;
    // simulation state
    private List<Sprite> mySprites;
    private double myCenterOfMassMagnitude = 0;
    private double myCenterOfMassExponent = 0;
    private Point2D myCenterOfMass = null;
    private List<Force> myForces;
    /**
     * Create a game of the given size with the given display for its shapes.
     * @param canvas used to create display
     */
    public Model (Canvas canvas) {
        myView = canvas;
        mySprites = new ArrayList<Sprite>();
        myForces = new ArrayList<Force>();
        ourBounds = myView.getSize();
    }
    
    /**
     * Finds mass closest to a certain point
     * @param point that is evaluated
     */
    public Mass closestMass(Point2D point) {
        Mass closest = null;
        for (Sprite mass : mySprites) {
            if (mass instanceof Mass && (closest == null || Point.distance(mass.getCenter().getX(), 
                mass.getCenter().getY(), 
                point.getX(), point.getY()) < Point.distance(closest.getCenter().getX(), 
                    closest.getCenter().getY(), point.getX(), point.getY()))) {
                closest = (Mass) mass;
            }
        }
        return closest;
    }
    
    /**
     * Resets global variables
     */
    public void clear() {
        mySprites = new ArrayList<Sprite>();
        myCenterOfMassMagnitude = 0;
        myCenterOfMassExponent = 0;
        myCenterOfMass = null;
    }
    
    
    /**
     * Draw all elements of the simulation.
     * @param pen used to paint shape on the screen
     */
    public void paint (Graphics2D pen) {
        for (Sprite s : mySprites) {
            s.paint(pen);
        }
    }
    
    /**
     * Returns center of mass as a Point2D object
     * 
     */
    public Point2D getCenterOfMass() {
        if (myCenterOfMass != null) {
            return (Point2D)myCenterOfMass.clone();
        }
        return null;
    }
    
    /**
     * Update simulation for this moment, given the time since the last moment.
     * @param elapsedTime time elapsed between each update
     */
    public void update (double elapsedTime) {
        CenterOfMassForce.centerOfMass();
        CenterOfMassForce.setCumulativeMassToZero();
        for (Sprite m : mySprites) {
            m.update(elapsedTime, ourBounds, myForces);
        }
    }

    /**
     * Add given mass or spring to this simulation.
     * @param massOrSpring object of either mass or spring
     */
    public void add (Sprite massOrSpring) {
        mySprites.add(massOrSpring);
    }
    
    /**
     * Remove given mass to this simulation.
     * @param massOrSpring object of type mass or spring
     */
    public void remove (Sprite massOrSpring) {
        mySprites.remove(massOrSpring);
    }
    
    /** */
    public void moveMasses() {
        for (Sprite mass : mySprites) {
            if (mass instanceof Mass) {
                if (mass.getCenter().getX() > ourBounds.width) {
                    mass.setCenter(new Location(mass.getCenter().getX() - Canvas.SHIFT_AMOUNT,
                                                mass.getCenter().getY()));
                }
                if (mass.getCenter().getY() > ourBounds.height) {
                    mass.setCenter(new Location(mass.getCenter().getX(),
                                                mass.getCenter().getY() - Canvas.SHIFT_AMOUNT));
                }
            }
        }
    }
    
    /**
     * Get centerOfMassMagnitude
     */
    public double getCenterOfMassMagnitude() {
        return myCenterOfMassMagnitude;
    }

    /**
     * Set centerOfMassMagnitude
     * @param centerOfMassMagnitude magnitude of center of mass force
     * @param centerOfMassExponent exponent of center of mass force
     */
    public void setCenterOfMass(double centerOfMassMagnitude, double centerOfMassExponent) {
        this.myCenterOfMassMagnitude = centerOfMassMagnitude;
        this.myCenterOfMassExponent = centerOfMassExponent;
    }

    /**
     * Get centerOfMassExponent
     */
    public double getCenterOfMassExponent() {
        return myCenterOfMassExponent;
    }
    
    /**
     * Gets list of forces
     */
    public List<Force> getForce () {
        return myForces;
    }
    

    /**
     * Add Force to myForces
     * @param f force to add
     */
    public void addForce (Force f) {
        if (f != null) {
            this.myForces.add(f);
        }
    }
    
    /**
     * Set height of view
     * @param height of view
     */
    public static void setHeight(int height) {
        ourBounds.setSize(ourBounds.width, Math.abs(height));
    }
    
    /**
     * Set width of view
     * @param width of view
     */
    public static void setWidth(int width) {
        ourBounds.setSize(Math.abs(width), ourBounds.height);
    }
    
    /**
     * Set height of view
     */
    public static int getHeight() {
        return ourBounds.height;
    }
    
    
    /**
     * Get width of view
     */
    public static int getWidth() {
        return ourBounds.width;
    }
    
}